home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Tools / freeze / modulefinder.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  13.9 KB  |  437 lines

  1. """Find modules used by a script, using introspection."""
  2.  
  3. import dis
  4. import imp
  5. import marshal
  6. import os
  7. import re
  8. import string
  9. import sys
  10.  
  11. if sys.platform=="win32":
  12.     # On Windows, we can locate modules in the registry with
  13.     # the help of the win32api package.
  14.     try:
  15.         import win32api
  16.     except ImportError:
  17.         print "The win32api module is not available - modules listed"
  18.         print "in the registry will not be found."
  19.         win32api = None
  20.  
  21.  
  22. IMPORT_NAME = dis.opname.index('IMPORT_NAME')
  23. IMPORT_FROM = dis.opname.index('IMPORT_FROM')
  24.  
  25. # Modulefinder does a good job at simulating Python's, but it can not
  26. # handle __path__ modifications packages make at runtime.  Therefore there
  27. # is a mechanism whereby you can register extra paths in this map for a
  28. # package, and it will be honoured.
  29.  
  30. # Note this is a mapping is lists of paths.
  31. packagePathMap = {}
  32.  
  33. # A Public interface
  34. def AddPackagePath(packagename, path):
  35.     paths = packagePathMap.get(packagename, [])
  36.     paths.append(path)
  37.     packagePathMap[packagename] = paths
  38.  
  39. class Module:
  40.  
  41.     def __init__(self, name, file=None, path=None):
  42.         self.__name__ = name
  43.         self.__file__ = file
  44.         self.__path__ = path
  45.         self.__code__ = None
  46.  
  47.     def __repr__(self):
  48.         s = "Module(%s" % `self.__name__`
  49.         if self.__file__ is not None:
  50.             s = s + ", %s" % `self.__file__`
  51.         if self.__path__ is not None:
  52.             s = s + ", %s" % `self.__path__`
  53.         s = s + ")"
  54.         return s
  55.  
  56.  
  57. class ModuleFinder:
  58.  
  59.     def __init__(self, path=None, debug=0, excludes = []):
  60.         if path is None:
  61.             path = sys.path
  62.         self.path = path
  63.         self.modules = {}
  64.         self.badmodules = {}
  65.         self.debug = debug
  66.         self.indent = 0
  67.         self.excludes = excludes
  68.  
  69.     def msg(self, level, str, *args):
  70.         if level <= self.debug:
  71.             for i in range(self.indent):
  72.                 print "   ",
  73.             print str,
  74.             for arg in args:
  75.                 print repr(arg),
  76.             print
  77.  
  78.     def msgin(self, *args):
  79.         level = args[0]
  80.         if level <= self.debug:
  81.             self.indent = self.indent + 1
  82.             apply(self.msg, args)
  83.  
  84.     def msgout(self, *args):
  85.         level = args[0]
  86.         if level <= self.debug:
  87.             self.indent = self.indent - 1
  88.             apply(self.msg, args)
  89.  
  90.     def run_script(self, pathname):
  91.         self.msg(2, "run_script", pathname)
  92.         fp = open(pathname)
  93.         stuff = ("", "r", imp.PY_SOURCE)
  94.         self.load_module('__main__', fp, pathname, stuff)
  95.  
  96.     def load_file(self, pathname):
  97.         dir, name = os.path.split(pathname)
  98.         name, ext = os.path.splitext(name)
  99.         fp = open(pathname)
  100.         stuff = (ext, "r", imp.PY_SOURCE)
  101.         self.load_module(name, fp, pathname, stuff)
  102.  
  103.     def import_hook(self, name, caller=None, fromlist=None):
  104.         self.msg(3, "import_hook", name, caller, fromlist)
  105.         parent = self.determine_parent(caller)
  106.         q, tail = self.find_head_package(parent, name)
  107.         m = self.load_tail(q, tail)
  108.         if not fromlist:
  109.             return q
  110.         if m.__path__:
  111.             self.ensure_fromlist(m, fromlist)
  112.  
  113.     def determine_parent(self, caller):
  114.         self.msgin(4, "determine_parent", caller)
  115.         if not caller:
  116.             self.msgout(4, "determine_parent -> None")
  117.             return None
  118.         pname = caller.__name__
  119.         if caller.__path__:
  120.             parent = self.modules[pname]
  121.             assert caller is parent
  122.             self.msgout(4, "determine_parent ->", parent)
  123.             return parent
  124.         if '.' in pname:
  125.             i = string.rfind(pname, '.')
  126.             pname = pname[:i]
  127.             parent = self.modules[pname]
  128.             assert parent.__name__ == pname
  129.             self.msgout(4, "determine_parent ->", parent)
  130.             return parent
  131.         self.msgout(4, "determine_parent -> None")
  132.         return None
  133.  
  134.     def find_head_package(self, parent, name):
  135.         self.msgin(4, "find_head_package", parent, name)
  136.         if '.' in name:
  137.             i = string.find(name, '.')
  138.             head = name[:i]
  139.             tail = name[i+1:]
  140.         else:
  141.             head = name
  142.             tail = ""
  143.         if parent:
  144.             qname = "%s.%s" % (parent.__name__, head)
  145.         else:
  146.             qname = head
  147.         q = self.import_module(head, qname, parent)
  148.         if q:
  149.             self.msgout(4, "find_head_package ->", (q, tail))
  150.             return q, tail
  151.         if parent:
  152.             qname = head
  153.             parent = None
  154.             q = self.import_module(head, qname, parent)
  155.             if q:
  156.                 self.msgout(4, "find_head_package ->", (q, tail))
  157.                 return q, tail
  158.         self.msgout(4, "raise ImportError: No module named", qname)
  159.         raise ImportError, "No module named " + qname
  160.  
  161.     def load_tail(self, q, tail):
  162.         self.msgin(4, "load_tail", q, tail)
  163.         m = q
  164.         while tail:
  165.             i = string.find(tail, '.')
  166.             if i < 0: i = len(tail)
  167.             head, tail = tail[:i], tail[i+1:]
  168.             mname = "%s.%s" % (m.__name__, head)
  169.             m = self.import_module(head, mname, m)
  170.             if not m:
  171.                 self.msgout(4, "raise ImportError: No module named", mname)
  172.                 raise ImportError, "No module named " + mname
  173.         self.msgout(4, "load_tail ->", m)
  174.         return m
  175.  
  176.     def ensure_fromlist(self, m, fromlist, recursive=0):
  177.         self.msg(4, "ensure_fromlist", m, fromlist, recursive)
  178.         for sub in fromlist:
  179.             if sub == "*":
  180.                 if not recursive:
  181.                     all = self.find_all_submodules(m)
  182.                     if all:
  183.                         self.ensure_fromlist(m, all, 1)
  184.             elif not hasattr(m, sub):
  185.                 subname = "%s.%s" % (m.__name__, sub)
  186.                 submod = self.import_module(sub, subname, m)
  187.                 if not submod:
  188.                     raise ImportError, "No module named " + subname
  189.  
  190.     def find_all_submodules(self, m):
  191.         if not m.__path__:
  192.             return
  193.         modules = {}
  194.         suffixes = [".py", ".pyc", ".pyo"]
  195.         for dir in m.__path__:
  196.             try:
  197.                 names = os.listdir(dir)
  198.             except os.error:
  199.                 self.msg(2, "can't list directory", dir)
  200.                 continue
  201.             for name in names:
  202.                 mod = None
  203.                 for suff in suffixes:
  204.                     n = len(suff)
  205.                     if name[-n:] == suff:
  206.                         mod = name[:-n]
  207.                         break
  208.                 if mod and mod != "__init__":
  209.                     modules[mod] = mod
  210.         return modules.keys()
  211.  
  212.     def import_module(self, partname, fqname, parent):
  213.         self.msgin(3, "import_module", partname, fqname, parent)
  214.         try:
  215.             m = self.modules[fqname]
  216.         except KeyError:
  217.             pass
  218.         else:
  219.             self.msgout(3, "import_module ->", m)
  220.             return m
  221.         if self.badmodules.has_key(fqname):
  222.             self.msgout(3, "import_module -> None")
  223.             self.badmodules[fqname][parent.__name__] = None
  224.             return None
  225.         try:
  226.             fp, pathname, stuff = self.find_module(partname,
  227.                                                    parent and parent.__path__)
  228.         except ImportError:
  229.             self.msgout(3, "import_module ->", None)
  230.             return None
  231.         try:
  232.             m = self.load_module(fqname, fp, pathname, stuff)
  233.         finally:
  234.             if fp: fp.close()
  235.         if parent:
  236.             setattr(parent, partname, m)
  237.         self.msgout(3, "import_module ->", m)
  238.         return m
  239.  
  240.     def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
  241.         self.msgin(2, "load_module", fqname, fp and "fp", pathname)
  242.         if type == imp.PKG_DIRECTORY:
  243.             m = self.load_package(fqname, pathname)
  244.             self.msgout(2, "load_module ->", m)
  245.             return m
  246.         if type == imp.PY_SOURCE:
  247.             co = compile(fp.read()+'\n', pathname, 'exec')
  248.         elif type == imp.PY_COMPILED:
  249.             if fp.read(4) != imp.get_magic():
  250.                 self.msgout(2, "raise ImportError: Bad magic number", pathname)
  251.                 raise ImportError, "Bad magic number in %s", pathname
  252.             fp.read(4)
  253.             co = marshal.load(fp)
  254.         else:
  255.             co = None
  256.         m = self.add_module(fqname)
  257.         m.__file__ = pathname
  258.         if co:
  259.             m.__code__ = co
  260.             self.scan_code(co, m)
  261.         self.msgout(2, "load_module ->", m)
  262.         return m
  263.  
  264.     def scan_code(self, co, m):
  265.         code = co.co_code
  266.         n = len(code)
  267.         i = 0
  268.         lastname = None
  269.         while i < n:
  270.             c = code[i]
  271.             i = i+1
  272.             op = ord(c)
  273.             if op >= dis.HAVE_ARGUMENT:
  274.                 oparg = ord(code[i]) + ord(code[i+1])*256
  275.                 i = i+2
  276.             if op == IMPORT_NAME:
  277.                 name = lastname = co.co_names[oparg]
  278.                 if not self.badmodules.has_key(lastname):
  279.                     try:
  280.                         self.import_hook(name, m)
  281.                     except ImportError, msg:
  282.                         self.msg(2, "ImportError:", str(msg))
  283.                         if not self.badmodules.has_key(name):
  284.                             self.badmodules[name] = {}
  285.                         self.badmodules[name][m.__name__] = None
  286.             elif op == IMPORT_FROM:
  287.                 name = co.co_names[oparg]
  288.                 assert lastname is not None
  289.                 if not self.badmodules.has_key(lastname):
  290.                     try:
  291.                         self.import_hook(lastname, m, [name])
  292.                     except ImportError, msg:
  293.                         self.msg(2, "ImportError:", str(msg))
  294.                         fullname = lastname + "." + name
  295.                         if not self.badmodules.has_key(fullname):
  296.                             self.badmodules[fullname] = {}
  297.                         self.badmodules[fullname][m.__name__] = None
  298.             else:
  299.                 lastname = None
  300.         for c in co.co_consts:
  301.             if isinstance(c, type(co)):
  302.                 self.scan_code(c, m)
  303.  
  304.     def load_package(self, fqname, pathname):
  305.         self.msgin(2, "load_package", fqname, pathname)
  306.         m = self.add_module(fqname)
  307.         m.__file__ = pathname
  308.         m.__path__ = [pathname]
  309.  
  310.         # As per comment at top of file, simulate runtime __path__ additions.
  311.         m.__path__ = m.__path__ + packagePathMap.get(fqname, [])
  312.  
  313.         fp, buf, stuff = self.find_module("__init__", m.__path__)
  314.         self.load_module(fqname, fp, buf, stuff)
  315.         self.msgout(2, "load_package ->", m)
  316.         return m
  317.  
  318.     def add_module(self, fqname):
  319.         if self.modules.has_key(fqname):
  320.             return self.modules[fqname]
  321.         self.modules[fqname] = m = Module(fqname)
  322.         return m
  323.  
  324.     def find_module(self, name, path):
  325.         if name in self.excludes:
  326.             self.msgout(3, "find_module -> Excluded")
  327.             raise ImportError, name
  328.  
  329.         if path is None:
  330.             if name in sys.builtin_module_names:
  331.                 return (None, None, ("", "", imp.C_BUILTIN))
  332.  
  333.             # Emulate the Registered Module support on Windows.
  334.             if sys.platform=="win32" and win32api is not None:
  335.                 HKEY_LOCAL_MACHINE = 0x80000002
  336.                 try:
  337.                     pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name))
  338.                     fp = open(pathname, "rb")
  339.                     # XXX - To do - remove the hard code of C_EXTENSION.
  340.                     stuff = "", "rb", imp.C_EXTENSION
  341.                     return fp, pathname, stuff
  342.                 except win32api.error:
  343.                     pass
  344.  
  345.             path = self.path
  346.         return imp.find_module(name, path)
  347.  
  348.     def report(self):
  349.         print
  350.         print "  %-25s %s" % ("Name", "File")
  351.         print "  %-25s %s" % ("----", "----")
  352.         # Print modules found
  353.         keys = self.modules.keys()
  354.         keys.sort()
  355.         for key in keys:
  356.             m = self.modules[key]
  357.             if m.__path__:
  358.                 print "P",
  359.             else:
  360.                 print "m",
  361.             print "%-25s" % key, m.__file__ or ""
  362.  
  363.         # Print missing modules
  364.         keys = self.badmodules.keys()
  365.         keys.sort()
  366.         for key in keys:
  367.             # ... but not if they were explicitely excluded.
  368.             if key not in self.excludes:
  369.                 mods = self.badmodules[key].keys()
  370.                 mods.sort()
  371.                 print "?", key, "from", string.join(mods, ', ')
  372.  
  373.  
  374. def test():
  375.     # Parse command line
  376.     import getopt
  377.     try:
  378.         opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:")
  379.     except getopt.error, msg:
  380.         print msg
  381.         return
  382.  
  383.     # Process options
  384.     debug = 1
  385.     domods = 0
  386.     addpath = []
  387.     exclude = []
  388.     for o, a in opts:
  389.         if o == '-d':
  390.             debug = debug + 1
  391.         if o == '-m':
  392.             domods = 1
  393.         if o == '-p':
  394.             addpath = addpath + string.split(a, os.pathsep)
  395.         if o == '-q':
  396.             debug = 0
  397.         if o == '-x':
  398.             exclude.append(a)
  399.  
  400.     # Provide default arguments
  401.     if not args:
  402.         script = "hello.py"
  403.     else:
  404.         script = args[0]
  405.  
  406.     # Set the path based on sys.path and the script directory
  407.     path = sys.path[:]
  408.     path[0] = os.path.dirname(script)
  409.     path = addpath + path
  410.     if debug > 1:
  411.         print "path:"
  412.         for item in path:
  413.             print "   ", `item`
  414.  
  415.     # Create the module finder and turn its crank
  416.     mf = ModuleFinder(path, debug, exclude)
  417.     for arg in args[1:]:
  418.         if arg == '-m':
  419.             domods = 1
  420.             continue
  421.         if domods:
  422.             if arg[-2:] == '.*':
  423.                 mf.import_hook(arg[:-2], None, ["*"])
  424.             else:
  425.                 mf.import_hook(arg)
  426.         else:
  427.             mf.load_file(arg)
  428.     mf.run_script(script)
  429.     mf.report()
  430.  
  431.  
  432. if __name__ == '__main__':
  433.     try:
  434.         test()
  435.     except KeyboardInterrupt:
  436.         print "\n[interrupt]"
  437.